package com.xunk.system.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.xunk.common.core.constant.CacheConstants;
import com.xunk.common.core.constant.RuyingConstants;
import com.xunk.common.core.enums.DeviceCategoryEnum;
import com.xunk.common.core.enums.DeviceEnum;
import com.xunk.common.core.enums.SupplierEnum;
import com.xunk.common.core.exception.CustomException;
import com.xunk.common.core.utils.*;
import com.xunk.common.core.web.domain.AjaxResult;
import com.xunk.common.redis.service.RedisService;
import com.xunk.equipment.api.RemoteEquipmentService;
import com.xunk.system.api.domain.SysHome;
import com.xunk.system.config.BuzConfig;
import com.xunk.system.domain.SysDevice;
import com.xunk.system.domain.SysDeviceBaseInfo;
import com.xunk.system.mapper.SysDeviceBaseInfoMapper;
import com.xunk.system.mapper.SysDeviceMapper;
import com.xunk.system.mapper.SysHomeMapper;
import com.xunk.system.service.ISysDeviceService;
import com.xunk.system.vo.RoomDeviceInfo;
import com.xunk.system.vo.SysDeviceBaseVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 设备Service业务层处理
 *
 * @author ruoyi
 * @date 2021-03-17
 */
@Service
@Slf4j
public class SysDeviceServiceImpl implements ISysDeviceService {

    @Autowired
    private SysDeviceMapper sysDeviceMapper;

    @Autowired
    private SysHomeMapper sysHomeMapper;

    @Autowired
    SysDeviceBaseInfoMapper sysDeviceBaseInfoMapper;

    @Autowired
    BuzConfig buzConfig;

    @Autowired
    RedisService redisService;

    @Autowired
    RemoteEquipmentService remoteEquipmentService;

    /**
     * 查询设备
     *
     * @param deviceSn 设备ID
     * @return 设备
     */
    @Override
    public AjaxResult selectSysDeviceById(String deviceSn) {
        return AjaxResult.success(sysDeviceMapper.selectSysDeviceById(deviceSn));
    }

    /**
     * 查询设备列表
     *
     * @param sysDevice 设备
     * @return 设备
     */
    @Override
    public List<SysDevice> selectSysDeviceList(SysDevice sysDevice) {
        return sysDeviceMapper.selectSysDeviceList(sysDevice);
    }

    /**
     * 新增设备
     * 如存在未删除的相同设备则添加失败
     *
     * @param sysDevice 设备
     * @return 结果
     */
    @Override
    @Transactional
    public int insertSysDevice(SysDevice sysDevice) {
        sysDevice.setCreateTime(DateUtils.getNowDate());

        int sys_num = sysDeviceMapper.selectExistsSysDeviceById(sysDevice.getDeviceSn());

        if (sys_num != 0)
            throw new CustomException("存在未删除的相同设备，请确认！");

        improveDevice(sysDevice);
        int i = sysDeviceMapper.insertSysDevice(sysDevice);
        bindDeviceByPlatform(sysDevice);
        return i;
    }

    private void improveDevice(SysDevice sysDevice) {

        SysDeviceBaseInfo sysDeviceBaseInfo = sysDeviceBaseInfoMapper.selectSysDeviceBaseInfoById(sysDevice.getDeviceBaseId());
        sysDevice.setDeviceName(sysDeviceBaseInfo.getBaseName());
        sysDevice.setDeviceType(sysDeviceBaseInfo.getBaseTypeCode());
        sysDevice.setDeviceSource(sysDeviceBaseInfo.getBaseSource());
        sysDevice.setDeviceNickname(sysDeviceBaseInfo.getBaseName());
        sysDevice.setDeviceCategory(sysDeviceBaseInfo.getBaseCategory());
        sysDevice.setSupplier(sysDeviceBaseInfo.getSupplierCode());
        sysDevice.setManufacturerNo(sysDeviceBaseInfo.getSupplierNumber());
        sysDevice.setSpecModel(sysDeviceBaseInfo.getSpecModel());
    }

    private void bindDeviceByPlatform(SysDevice sysDevice) {
        // 绑定如影设备 (只有是网关设备时才会调用如影的添加设备接口)
        SupplierEnum supplierEnum = SupplierEnum.parseSupplierEnum(sysDevice.getSupplier());
        DeviceCategoryEnum deviceCategoryEnum = DeviceCategoryEnum.parseDeviceCategoryEnum(sysDevice.getDeviceCategory());
        if (Objects.equals(supplierEnum, SupplierEnum.RUYING) && Objects.equals(deviceCategoryEnum, DeviceCategoryEnum.GATEWAY)) {
            // 根据家的id查询ownerid
            Long deptId = sysDevice.getDeptId();
            String ownerId = sysHomeMapper.selectOwnerIdFromDeptByDeptId(deptId);
            XunkAssert.notEmpty(ownerId, "设备所在区域没有唯一标识");

            // 绑定如影设备
            String addDeviceUrl = buzConfig.getRuyingIotApiUrl() + RuyingConstants.RUYING_DEVICE_ADD;
            Map<String, Object> params = new HashMap<>();
            List<Map<String, Object>> deviceList = new ArrayList<>();
            params.put("devices", deviceList);
            Map<String, Object> device1 = new HashMap<>();
            String deviceSn = sysDevice.getDeviceSn();
            device1.put("did", deviceSn);
            device1.put("token", sysDevice.getDeviceToken());
            deviceList.add(device1);
            String jwtToken = JwtUtil.getRuyingGwt(buzConfig.getRuyingIotSerectKey(), ownerId);
            XunkAssert.notEmpty(jwtToken, "token生成为空");

            // 配置请求header参数
            Map<String, String> headers = HttpUtil.getHeader4Ruying(buzConfig.getRuyingIotAppId(), jwtToken);
            // 调用如影添加设备接口
            JSONObject addDeviceResponse = HttpUtil.doPoststr(addDeviceUrl, JSON.toJSONString(params), headers);
            Optional<JSONObject> responseOp = Optional.ofNullable(addDeviceResponse).map(s -> s.getJSONObject("data")).map(s -> s.getJSONArray("devices")).map(s -> s.getJSONObject(0));
            Integer status = responseOp.map(s -> s.getInteger("status")).orElse(null);
            String description = responseOp.map(s -> "如影平台，设备 " + s.getString("description")).orElse("如影平台，添加设备异常");
            XunkAssert.isTrue(Objects.equals(status, 0), description);

            // 读取当前设备信息
            String readDeviceUrl = buzConfig.getRuyingIotApiUrl() + MessageFormat.format(RuyingConstants.RUYING_DEVICE_READ, deviceSn);
            JSONObject readDeviceResponse = HttpUtil.doGetstr(readDeviceUrl, headers);
            String deviceType = Optional.ofNullable(readDeviceResponse).map(s -> s.getJSONObject("data")).map(s -> s.getJSONObject("device")).map(s -> s.getJSONObject("summary")).map(s -> s.getString("type")).orElse(null);
            XunkAssert.notEmpty(deviceType, "查询设备type为空");

            // 查询设备定义
            String defineDeviceUrl = buzConfig.getRuyingIotSpecUrl() + MessageFormat.format(RuyingConstants.RUYING_DEVICE_DEFINE, deviceType);
            JSONObject defineDeviceResponse = HttpUtil.doGetstr(defineDeviceUrl);
            JSONObject defineDeviceData = Optional.ofNullable(defineDeviceResponse).map(s -> s.getJSONObject("data")).orElse(null);
            XunkAssert.notEmpty(defineDeviceData, "读取如影设备定义异常，type = " + deviceType);
//
//            // 存入数据库
//            RuyingDeviceDO deviceDO = copyDevice(deviceDefine);
//            List<RuyingDeviceServiceDO> deviceServiceDOS = copyDeviceServices(deviceDefine);
//            List<RuyingDevicePropertyDO> devicePropertyDOS = copyDeviceProperties(deviceDefine);
//
//            sysDeviceMapper.insertRuyingDevice(deviceDO);
//            sysDeviceMapper.insertRuyingDeviceServices(deviceServiceDOS);
//            sysDeviceMapper.insertRuyingDeviceProperties(devicePropertyDOS);
//
            defineDeviceData.put("did", deviceSn);
            // 存储到redis中
            redisService.setCacheObject(MessageFormat.format(CacheConstants.RUYING_DEVICE_DEFINE_CACHE, ownerId, deviceSn), JSON.toJSONString(defineDeviceData));
        }
    }

    /**
     * 修改设备
     *
     * @param sysDevice 设备
     * @return 结果
     */
    @Override
    public int updateSysDevice(SysDevice sysDevice) {
        if (sysDevice.getDeviceBaseId() != null) {
            improveDevice(sysDevice);
        }
        if (Objects.equals(sysDevice.getRoomNum(), "#")) {
            sysDevice.setRoomNum("");
        }
        int i = sysDeviceMapper.updateSysDevice(sysDevice);
        redisService.deleteObject(CacheConstants.EQUIPMENT_INFO_KEY + sysDevice.getDeviceSn());
        return i;
    }

    /**
     * 批量删除设备
     *
     * @param deviceSns 需要删除的设备ID
     * @return 结果
     */
    @Override
    @Transactional
    public int deleteSysDeviceByIds(String[] deviceSns) {
        // 查询这些设备的信息
        Arrays.stream(deviceSns).forEach(deviceSn -> {
            SysDevice sysDevice = sysDeviceMapper.selectSysDeviceById(deviceSn);
            SupplierEnum supplierEnum = SupplierEnum.parseSupplierEnum(sysDevice.getSupplier());
            if (Objects.equals(supplierEnum, SupplierEnum.RUYING)) {
                // 如影设备需要先解除网关设备
                XunkAssert.isTrue(Objects.equals(DeviceEnum.parseDeviceEnum(sysDevice.getDeviceType()), DeviceEnum.GATEWAY), "如影设备只支持删除网关设备");
                // 调用feign接口
                log.info("=====准备删除如影网关=====");
                AjaxResult ajaxResult = remoteEquipmentService.deleteRuyingDevice(deviceSn, sysDevice.getDeptId());
                log.info("=====删除完毕如影网关=====");
                XunkAssert.isTrue(ajaxResult.isSuccess(), "Feign 如影平台解除设备异常 :" + ajaxResult.getMsg());
            } else if (Objects.equals(supplierEnum, SupplierEnum.DNAKE)) {
                // 狄耐克设备删除网关时，子设备一块删除
                log.info("=====准备删除狄耐克网关子设备=====");
                sysDeviceMapper.deleteSysDeviceByGatewayId(deviceSn, SupplierEnum.DNAKE.getSupplierName());
                log.info("=====删除完毕狄耐克网关子设备=====");
            }
            sysDeviceMapper.deleteSysDeviceById(deviceSn);
            // 删除对应的子设备缓存
            List<String> strings = sysDeviceMapper.listChildDeviceSnsByParentSn(deviceSn);
            if (DataUtils.isNotEmpty(strings)) {
                strings.forEach(s -> redisService.deleteObject(CacheConstants.EQUIPMENT_INFO_KEY + s));
            }
            redisService.deleteObject(CacheConstants.EQUIPMENT_INFO_KEY + deviceSn);
        });
        return 1;
    }

    /**
     * 删除设备信息
     *
     * @param deviceSn 设备ID
     * @return 结果
     */
    @Override
    public int deleteSysDeviceById(String deviceSn) {
        return sysDeviceMapper.deleteSysDeviceById(deviceSn);
    }

    @Override
    public List<RoomDeviceInfo> getDeviceInfo(Long homeId, String trigger) {

        SysHome sysHome = sysHomeMapper.selectSysHomeById(homeId);
        String description = sysHome.getDescription();
        List<SysDeviceBaseVO> devices = sysDeviceMapper.selectDeviceInfoByHomeId(trigger, homeId);

        boolean t = Objects.equals(trigger, "1");

        try {
            Map<String, String> roomMap = parseDescription(description);
            Map<String, List<SysDeviceBaseVO>> collect = devices.stream().peek(item -> {
                if (DataUtils.isEmpty(item.getRoomNum())) {
                    item.setRoomNum("#");
                }
                if (DataUtils.isNotEmpty(item.getBaseId()) && t) {
                    item.setCandidateModel(getCandidateModel(item.getBaseId()));
                }
                String roomName = roomMap.getOrDefault(item.getRoomNum(), "#");
                item.setRoomName(roomName);
            }).collect(Collectors.groupingBy(SysDeviceBaseVO::getRoomNum, LinkedHashMap::new, Collectors.toList()));

            List<RoomDeviceInfo> result = collect.entrySet().stream().map(item -> {
                RoomDeviceInfo info = new RoomDeviceInfo();
                info.setDevices(item.getValue());
                info.setRoomNum(item.getKey());
                info.setRoomName(roomMap.getOrDefault(item.getKey(), "#"));
                info.setDeviceNum(item.getValue().size());
                roomMap.remove(item.getKey());
                return info;
            }).collect(Collectors.toList());
            if (MapUtils.isNotEmpty(roomMap)) {
                List<RoomDeviceInfo> temp = result;
                result = new ArrayList<>(temp.stream().filter(s -> !Objects.equals(s.getRoomName(), "#")).collect(Collectors.toList()));
                for (Map.Entry<String, String> entry : roomMap.entrySet()) {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    RoomDeviceInfo info = new RoomDeviceInfo();
                    info.setDeviceNum(0);
                    info.setRoomNum(key);
                    info.setRoomName(value);
                    result.add(info);
                }
                result.addAll(temp.stream().filter(s -> Objects.equals(s.getRoomName(), "#")).collect(Collectors.toList()));
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException("房屋房间信息解析失败!");
        }
    }

    public static String MODEL_KEY = "hainayun:model:";

    public Map<String, Object> getCandidateModel(Long id) {
        if (DataUtils.isEmpty(id)) {
            return new HashMap<>();
        }
        String key = MODEL_KEY + id;
        Map<String, Object> cacheObject = redisService.getCacheObject(key);
        if (Objects.nonNull(cacheObject)) {
            return cacheObject;
        }
        SysDeviceBaseInfo baseInfo = sysDeviceBaseInfoMapper.selectSysDeviceBaseInfoById(id);
        Map<String, Object> cache;
        if (DataUtils.isNotEmpty(baseInfo) && DataUtils.isNotEmpty(baseInfo.getCandidateModel())) {
            cache = JSON.parseObject(baseInfo.getCandidateModel(), new TypeReference<Map<String, Object>>() {
            });
        } else {
            cache = new HashMap<>();
        }
        redisService.setCacheObject(key, cache, 12, TimeUnit.HOURS);
        return cache;
    }


    private Map<String, String> parseDescription(String description) {
        if (DataUtils.isEmpty(description)) {
            return new HashMap<>();
        }
        return JSON.parseObject(description, new TypeReference<Map<String, String>>() {
        });
    }
}
